package bll

import (
  "context"
  "encoding/json"
  "fmt"
  "gitlab.local/backend/proto/dto"
  "manager/auth"
  "manager/client/grpc"
  log "manager/collector/logger"
  "manager/config"
  "manager/errors"
  "manager/lib/infra"
  "manager/lib/trace_log"
  "manager/model/po"
  "manager/store/postgres"
  "manager/utils"
  "time"

  "manager/event"
  "manager/model"
  "manager/model/entity"
  "manager/store"
)

type setting struct {
  iSetting store.ISetting
}

var Setting = &setting{
  iSetting: postgres.Setting,
}

func (a *setting) init() func() {
  return func() {}
}

func (a *setting) onEvent(data *event.Data) {
  if data == nil || (data.Type != event.Event_Bind_Bike && data.Type != event.Event_UnBind_Bike && data.Type != event.Event_Bike_Cmd) {
    return
  }

  log.Info("---------------- userBikeSetting onEvent data:", utils.ToJsonString(data))
  switch data.Type {
  case event.Event_Bind_Bike:
    a.createSettings(data.Data)
  case event.Event_UnBind_Bike:
    // 解析参数
    obj := data.Data.(*event.UnBindBike)
    if obj == nil {
      log.Error("setting onEvent err")
      return
    }

    // 删除配置
    a.Delete(context.Background(), &model.SettingDeleteRequest{EbikeId: obj.BikeId})
  case event.Event_Bike_Cmd:
    // 解析参数
    obj := data.Data.(*event.BikeCmd)
    if obj == nil {
      log.Error("setting onEvent err")
      return
    }

    // 参数校验
    if obj.EbikeId == 0 || obj.CmdType == "" {
      log.Errorf("setting onEvent Create default setting params error userId:%v CmdType:%v", obj.EbikeId, obj.CmdType)
      return
    }

    a.updateSettings(obj.EbikeId, obj.CmdType, obj.Body)
  }

}

// updateSettings 更新设置
func (a *setting) updateSettings(bikeId int64, cmd string, data interface{}) {
  switch cmd {
  case "cmd":
    obj := data.(*dto.CommandRequest)
    if obj == nil {
      return
    }

  case "toggle":
    obj := data.(*dto.SwitchRequest)
    if obj == nil {
      return
    }

    update := &model.SettingUpdateRequest{
      EbikeId:         &bikeId,
      EbikeSet:        &model.EbikeSet{},
      HillAssist:      nil,
      HillAssistRange: nil,
      FlyCar:          nil,
      MsgSet:          &model.MsgSet{},
      Type:            nil,
    }

    open := 1
    zero := 0
    // 上坡辅助
    if obj.HillAssist != nil {
      hillAssist := int(*obj.HillAssist)
      hillAssistRange := int(*obj.HillAssist)
      if hillAssist == 0 {
        update.HillAssist = &zero
      }

      if hillAssist > 0 {
        update.HillAssist = &open
        update.HillAssistRange = &hillAssistRange
      }
    }

    // 无感开锁
    if obj.SenselessUnlock != nil {
      unlockValue := int(*obj.SenselessUnlock)
      if unlockValue == 0 {
        update.EbikeSet.OpenLock = &zero
      }

      if unlockValue > 0 {
        update.EbikeSet.OpenLock = &open
        update.EbikeSet.OpenLockDistance = &unlockValue
      }
    }

    // 防飞车
    if obj.PreventOutOfControl != nil {
      value := int(*obj.PreventOutOfControl)
      update.FlyCar = &value
    }

    // 报警器震动灵敏度（震动），等级1~3，值越大越灵敏。
    if obj.VibrationSensitivity != nil {
      value := int(*obj.VibrationSensitivity)
      update.EbikeSet.VibrationSensitivity = &value
    }

    a.Update(context.Background(), update)
  case "set":
    obj := data.(*dto.SettingRequest)
    if obj == nil {
      return
    }

    update := &model.SettingUpdateRequest{
      EbikeId:         &bikeId,
      EbikeSet:        &model.EbikeSet{},
      HillAssist:      nil,
      HillAssistRange: nil,
      FlyCar:          nil,
      MsgSet:          &model.MsgSet{},
      Type:            nil,
    }

    open := 1
    zero := 0

    // 自动锁车
    if obj.AutoLockTime != nil {
      value := int(*obj.AutoLockTime)
      if value == 0 {
        update.EbikeSet.LeaveLock = &zero
      }

      if value > 0 {
        update.EbikeSet.LeaveLock = &open
        update.EbikeSet.LeaveCountdown = &value
      }
    }

    a.Update(context.Background(), update)
  }
}

// createSettings 创建设置
func (a *setting) createSettings(data interface{}) error {
  // 解析参数
  obj := data.(*event.BindBike)
  if obj == nil {
    log.Error("setting onEvent err")
    return nil
  }

  // 参数校验
  if obj.BikeId == 0 || obj.UserId == 0 {
    log.Errorf("setting onEvent Create default setting params error userId:%v bikeId:%v", obj.UserId, obj.BikeId)
    return nil
  }

  // 创建车辆默认配置
  if err := a.Create(context.Background(), &model.SettingCreateRequest{
    UserId:  obj.UserId,
    EbikeId: obj.BikeId,
  }); err != nil {
    log.Errorf("setting onEvent Create default setting error userId:%v bikeId:%v", obj.UserId, obj.BikeId)
    return nil
  }
  return nil
}

func (a *setting) Create(ctx context.Context, in *model.SettingCreateRequest) error {
  var (
    err error
  )

  c := buildSetting(in.UserId, in)
  _, err = a.iSetting.Create(c)
  return err
}

func (a *setting) Update(ctx context.Context, in *model.SettingUpdateRequest) error {
  var (
    dict = make(map[string]interface{})
  )

  // todo:校验车辆、校验用户和车辆信息
  if in.EbikeId != nil && !Ebike.IsValid(*in.EbikeId) {
    return errors.New("bike is not valid")
  }

  if in.EbikeId != nil {
    bikeInfo, _ := CacheBike.GetBikeInfoById(*in.EbikeId)
    if bikeInfo != nil && bikeInfo.SNCode != "" {
      trace_log.DebugLogger(bikeInfo.SNCode, "setting update req:", utils.ToJsonString(in))
    }
  }

  // todo:加事务
  if in.EbikeId != nil {
    dict["ebike_id"] = in.EbikeId
  }

  if in.HillAssist != nil {
    dict["hill_assist"] = in.HillAssist
  }

  // do other update here
  if in.HillAssist != nil {
    dict["hill_assist"] = in.HillAssist
  }

  if in.HillAssistRange != nil {
    dict["hill_assist_range"] = in.HillAssistRange
  }

  if in.FlyCar != nil {
    dict["fly_car"] = in.FlyCar
  }

  if in.Type != nil {
    dict["type"] = in.Type
  }

  // 先读取json数据
  var settings *entity.Setting
  if in.MsgSet != nil || in.EbikeSet != nil {
    var err error
    settings, err = a.iSetting.Find(&model.SettingUpdateRequest{
      Id:      in.Id,
      EbikeId: in.EbikeId,
    })
    if err != nil {
      return err
    }
  }

  if in.MsgSet != nil && settings != nil && settings.MsgSet != nil {
    if in.MsgSet.Shake != nil {
      settings.MsgSet.Shake = *(in.MsgSet.Shake)
    }
    if in.MsgSet.BlackOut != nil {
      settings.MsgSet.BlackOut = *(in.MsgSet.BlackOut)
    }
    if in.MsgSet.Hitchs != nil {
      settings.MsgSet.Hitchs = *(in.MsgSet.Hitchs)
    }
    if in.MsgSet.Handrail != nil {
      settings.MsgSet.Handrail = *(in.MsgSet.Handrail)
    }
    if in.MsgSet.StealAlarm != nil {
      settings.MsgSet.StealAlarm = *(in.MsgSet.StealAlarm)
    }
    if in.MsgSet.ChargeMonitor != nil {
      settings.MsgSet.ChargeMonitor = *(in.MsgSet.ChargeMonitor)
    }
    dict["MsgSet"] = settings.MsgSet
  }

  if in.EbikeSet != nil && settings != nil {
    if in.EbikeSet.PowerWarn != nil {
      settings.EbikeSet.PowerWarn = *(in.EbikeSet.PowerWarn)
    }
    if in.EbikeSet.PowerValue != nil {
      settings.EbikeSet.PowerValue = *(in.EbikeSet.PowerValue)
    }
    if in.EbikeSet.LeaveLock != nil {
      settings.EbikeSet.LeaveLock = *(in.EbikeSet.LeaveLock)
    }
    if in.EbikeSet.LeaveCountdown != nil {
      settings.EbikeSet.LeaveCountdown = *(in.EbikeSet.LeaveCountdown)
    }
    if in.EbikeSet.OpenLock != nil {
      settings.EbikeSet.OpenLock = *(in.EbikeSet.OpenLock)
    }
    if in.EbikeSet.OpenLockDistance != nil {
      settings.EbikeSet.OpenLockDistance = *(in.EbikeSet.OpenLockDistance)
    }
    if in.EbikeSet.HitchsUp != nil {
      settings.EbikeSet.HitchsUp = *(in.EbikeSet.HitchsUp)
    }
    if in.EbikeSet.Alarm != nil {
      settings.EbikeSet.Alarm = *(in.EbikeSet.Alarm)
    }
    if in.EbikeSet.VibrationSensitivity != nil {
      settings.EbikeSet.VibrationSensitivity = *(in.EbikeSet.VibrationSensitivity)
    }
    if in.EbikeSet.ElectronicFence != nil {
      settings.EbikeSet.ElectronicFence = *(in.EbikeSet.ElectronicFence)
    }
    if in.EbikeSet.FenceCenter != nil {
      settings.EbikeSet.FenceCenter = po.Point{
        X: in.EbikeSet.FenceCenter.X,
        Y: in.EbikeSet.FenceCenter.Y,
      }
    }
    if in.EbikeSet.FenceRadius != nil {
      settings.EbikeSet.FenceRadius = *(in.EbikeSet.FenceRadius)
    }
    dict["ebike_set"] = settings.EbikeSet
  }

  return a.iSetting.Update(in.Id, dict)
}

func (a *setting) Delete(ctx context.Context, in *model.SettingDeleteRequest) error {
  if in.EbikeId > 0 {
    info, _ := a.iSetting.Find(&model.SettingUpdateRequest{
      EbikeId: &in.EbikeId,
    })

    if info.Id == 0 {
      return fmt.Errorf("setting is not exist")
    }

    return a.iSetting.Delete(info.Id)
  }

  return a.iSetting.Delete(in.Id)
}

func (a *setting) List(ctx context.Context, in *model.SettingListRequest) (*model.SettingListResponse, error) {
  var (
    err   error
    total int
    list  []*entity.Setting
    out   = &model.SettingListResponse{}
  )

  // todo: 检查车辆是否有效
  if !Ebike.IsValid(in.EbikeId) {
    return nil, errors.EbikeNotExist.Error()
  }

  if total, list, err = a.iSetting.List(in); err != nil {
    return nil, err
  }

  // todo: 判断是否存在，不存在就创建设置， 临时这么设计
  if total == 0 {
    userId, _ := auth.ContextUserID(ctx)
    a.createSettings(&event.BindBike{
      BikeId: in.EbikeId,
      UserId: userId,
    })
    // 查询列表
    if total, list, err = a.iSetting.List(in); err != nil {
      return nil, err
    }
  }

  out.Total = total
  out.List = model.SettingsEntityToDto(list)
  return out, nil
}

// IsOverElectronicFence 是否超出电子围栏
func (a *setting) IsOverElectronicFence(sn string, bikeId int64) bool {
  settings, _ := postgres.Setting.Find(&model.SettingUpdateRequest{
    EbikeId: &bikeId,
  })

  bikeInfo, _ := CacheBike.GetBikeInfoById(settings.EbikeId)
  if bikeInfo.SNCode == "" {
    trace_log.DebugLogger(sn, "bikeInfo.SNCode is empty")
    return false
  }

  // 围栏大小是否有变更
  if settings.EbikeSet.ElectronicFence == 0 {
    trace_log.DebugLogger(sn, "settings.EbikeSet.ElectronicFence is 0")
    return false
  }
  // 获取当前车辆位置，和围栏中心点位置，计算是否在围栏内
  // 获取车辆位置
  rsp, _ := grpc.SCmd.GetShadowAttributeKeys(context.Background(), &dto.GetShadowAttributeKeysRequest{
    DeviceName: bikeInfo.SNCode,
    Key:        "location",
  })

  var point po.Point
  err := json.Unmarshal([]byte(rsp.Value), &point)
  if err != nil {
    trace_log.DebugLogger(sn, "json.Unmarshal error", err)
    return false
  }

  // 计算距离
  distance := infra.Lbs.GeoDistance(settings.EbikeSet.FenceCenter.X, settings.EbikeSet.FenceCenter.Y, point.X, point.Y, "K")

  // 默认50米
  if settings.EbikeSet.FenceRadius == 0 {
    settings.EbikeSet.FenceRadius = 10000
  }

  trace_log.DebugLogger(sn, "distance", distance, " FenceRadius:", settings.EbikeSet.FenceRadius, "point:", utils.ToJsonString(point))

  return int(distance) >= settings.EbikeSet.FenceRadius
}

func buildSetting(userId int64, in *model.SettingCreateRequest) *entity.Setting {
  // todo: check the entity is required
  return &entity.Setting{

    UserId: userId,

    EbikeId: in.EbikeId,

    EbikeSet: &po.EbikeSet{
      PowerWarn:            config.Conf.SystemSetting.EbikeSet.PowerWarn,
      PowerValue:           config.Conf.SystemSetting.EbikeSet.PowerValue,
      LeaveLock:            config.Conf.SystemSetting.EbikeSet.LeaveLock,
      LeaveCountdown:       config.Conf.SystemSetting.EbikeSet.LeaveCountdown,
      OpenLock:             config.Conf.SystemSetting.EbikeSet.OpenLock,
      OpenLockDistance:     config.Conf.SystemSetting.EbikeSet.OpenLockDistance,
      HitchsUp:             config.Conf.SystemSetting.EbikeSet.HitchsUp,
      Alarm:                config.Conf.SystemSetting.EbikeSet.Alarm,
      VibrationSensitivity: config.Conf.SystemSetting.EbikeSet.VibrationSensitivity,
      ElectronicFence:      config.Conf.SystemSetting.EbikeSet.ElectronicFence,
      FenceCenter: po.Point{
        config.Conf.SystemSetting.EbikeSet.FenceCenter.X,
        config.Conf.SystemSetting.EbikeSet.FenceCenter.Y,
      },
      FenceRadius: config.Conf.SystemSetting.EbikeSet.FenceRadius,
    },

    HillAssist: config.Conf.SystemSetting.HillAssist,

    HillAssistRange: config.Conf.SystemSetting.HillAssistRange,

    FlyCar: config.Conf.SystemSetting.FlyCar,

    MsgSet: &po.MsgSet{
      StealAlarm:    config.Conf.SystemSetting.MsgSet.StealAlarm,
      Hitchs:        config.Conf.SystemSetting.MsgSet.Hitchs,
      Shake:         config.Conf.SystemSetting.MsgSet.Shake,
      BlackOut:      config.Conf.SystemSetting.MsgSet.BlackOut,
      ChargeMonitor: config.Conf.SystemSetting.MsgSet.ChargeMonitor,
      Handrail:      config.Conf.SystemSetting.MsgSet.Handrail,
    },

    Type: config.Conf.SystemSetting.Type,

    CreatedAt: time.Now().Unix(),

    UpdatedAt: time.Now().Unix(),
  }
}
